package com.rdr.common;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import java.io.Serializable;
import java.util.Map;
import java.util.Objects;

import static com.rdr.common.StringStyle.STYLE;

/**
 * 基础pojo类，所有pojo类（无论是Dto/Vo/Model/Do/Bo/Po等)都应继承此类
 */
public abstract class BasePojo implements Serializable {


    /**
     * 重写toString方法：用反射方式输出当前类（以及父类）的所有字段。
     */
    @Override
    public String toString() {
        // 这个builder也是烦，有些输出参数在这里指定，有些又在Style里指定。
        ReflectionToStringBuilder toStringBuilder = new ReflectionToStringBuilder(
                // 对当前对象做toString操作
                this,
                // toString的转换风格。自定义了一个
                STYLE,
                // 不用自定，内部会生成一个。
                null,
                // 上溯父类时，最多上溯到这个类。
                BasePojo.class,
                // 输出transients变量。代码中有些Map会被声明为transient的。想了想还是输出吧
                true,
                // 不输出静态变量。
                false,
                // 不输出null值
                true);
        return toStringBuilder.toString();
    }
}

/**
 * 自定义一个toString的实现类。基本上是参考commons-lang3的JSON_STYLE来的。
 * <p>
 * 修改了JSON_STYLE将文字按unicode输出的问题。按unicode输出文字会导致中文全变成\u6516这种格式，对trouble shooting完全无用。
 * 参见：“没有使用JsonStyle类中……”的注释
 */
class StringStyle extends ToStringStyle {

    static final StringStyle STYLE = new StringStyle();

    private static final long serialVersionUID = 1L;

    private static final String FIELD_NAME_QUOTE = "\"";

    /**
     * <p>
     * Constructor.
     * </p>
     *
     * <p>
     * Use the static constant rather than instantiating.
     * </p>
     */
    StringStyle() {
        super();

        this.setUseClassName(false);
        this.setUseIdentityHashCode(false);

        this.setContentStart("{");
        this.setContentEnd("}");

        this.setArrayStart("[");
        this.setArrayEnd("]");

        this.setFieldSeparator(",");
        this.setFieldNameValueSeparator(":");

        this.setNullText("null");

        this.setSummaryObjectStartText("\"<");
        this.setSummaryObjectEndText(">\"");

        this.setSizeStartText("\"<size=");
        this.setSizeEndText(">\"");
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName,
                       final Object[] array, final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName, final long[] array,
                       final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName, final int[] array,
                       final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName,
                       final short[] array, final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName, final byte[] array,
                       final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName, final char[] array,
                       final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName,
                       final double[] array, final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName,
                       final float[] array, final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName,
                       final boolean[] array, final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, array, fullDetail);
    }

    @Override
    public void append(final StringBuffer buffer, final String fieldName, final Object value,
                       final Boolean fullDetail) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        if (!isFullDetail(fullDetail)) {
            throw new UnsupportedOperationException(
                    "FullDetail must be true when using JsonToStringStyle");
        }

        super.append(buffer, fieldName, value, fullDetail);
    }

    @Override
    protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
        appendValueAsString(buffer, String.valueOf(value));
    }

    @Override
    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {

        if (value == null) {
            appendNullText(buffer, fieldName);
            return;
        }

        if (value instanceof String || value instanceof Character) {
            appendValueAsString(buffer, value.toString());
            return;
        }

        if (value instanceof Number || value instanceof Boolean) {
            buffer.append(value);
            return;
        }

        final String valueAsString = value.toString();
        if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
            buffer.append(value);
            return;
        }

        appendDetail(buffer, fieldName, valueAsString);
    }

    @Override
    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
        if (map != null && !map.isEmpty()) {
            buffer.append(getContentStart());

            boolean firstItem = true;
            for (final Map.Entry<?, ?> entry : map.entrySet()) {
                final String keyStr = Objects.toString(entry.getKey(), null);
                if (keyStr != null) {
                    if (firstItem) {
                        firstItem = false;
                    } else {
                        appendFieldEnd(buffer, keyStr);
                    }
                    appendFieldStart(buffer, keyStr);
                    final Object value = entry.getValue();
                    if (value == null) {
                        appendNullText(buffer, keyStr);
                    } else {
                        appendInternal(buffer, keyStr, value, true);
                    }
                }
            }

            buffer.append(getContentEnd());
            return;
        }

        buffer.append(map);
    }

    private boolean isJsonArray(final String valueAsString) {
        return valueAsString.startsWith(getArrayStart())
                && valueAsString.endsWith(getArrayEnd());
    }

    private boolean isJsonObject(final String valueAsString) {
        return valueAsString.startsWith(getContentStart())
                && valueAsString.endsWith(getContentEnd());
    }

    /**
     * Appends the given String enclosed in double-quotes to the given StringBuffer.
     *
     * @param buffer the StringBuffer to append the value to.
     * @param value  the value to append.
     */
    private void appendValueAsString(final StringBuffer buffer, final String value) {
        // 这里没有使用JsonStyle类中的StringEscapeUtils.escapeJson(value)，而是直接使用了value
        buffer.append('"').append(value).append('"');
    }

    @Override
    protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {

        if (fieldName == null) {
            throw new UnsupportedOperationException(
                    "Field names are mandatory when using JsonToStringStyle");
        }
        // 这里没有使用JsonStyle类中的StringEscapeUtils.escapeJson(fieldName)，而是直接使用了 fieldName
        super.appendFieldStart(buffer, FIELD_NAME_QUOTE + fieldName
                + FIELD_NAME_QUOTE);
    }

    /**
     * <p>
     * Ensure {@code Singleton} after serialization.
     * </p>
     *
     * @return the singleton
     */
    private Object readResolve() {
        return STYLE;
    }

}
